<template>
    <el-dialog v-model="dialog" title="批量上传" class="el-dialog" :close-on-click-modal="false" :show-close="false"
        :overflow="true">
        <div class="btn-box">
            <input type="file" class="input" @change="getFile" ref="inputFileRef">
            <input type="submit" value="渲染至页面" class="input" :class="{ dis: renderDis }" @click="uploadFile(props.uploadKeys)"
                :disabled="uploadDisabled">
            <input type="submit" value="批量更新" class="input" :class="{ dis: editDis }" :disabled="editDiasabled"
                @click="batchUpdate">
            <input type="submit" value="批量新增" class="input" :class="{ dis: editDis }" :disabled="editDiasabled"
                @click="batchAdd">

        </div>
        <el-button link type="success" @click="downloadTemplate">下载导入模板</el-button>
        <div class="table-box">
            <div class="tableLoading" v-show="tableLoading">
                <img src="../assets/loading.gif" alt="loading">
                <span>文件解析进度：{{ parseProgress }}%</span>
                <span>界面渲染进度：{{ renderProgress }}%</span>
            </div>            
            <slot></slot>
        </div>
        <div class="cbtn-box">
            <el-button type="primary" @click="closeDialog">关闭</el-button>
        </div>

    </el-dialog>
</template>

<script setup>
import { ref, reactive } from 'vue'
import * as XLSX from 'xlsx'
import { msgOpen } from '@/utils/msg'

//接受父组件传过来的表格列的key值数组
let props = defineProps({
    uploadKeys: Array,
    dateType: {
        type: String,
        default: 'date'
    }
})

let dialog = ref(false)
let inputFileRef = ref(null)
let uploadDisabled = ref(true)
let editDiasabled = ref(true)
let renderDis = ref(true)
let editDis = ref(true)
//打开
const open = () => dialog.value = true

//关闭
const close = () => {
    dialog.value = false
    tableData = reactive({})
    excelData = ref([])
    inputFileRef.value.value = null; // 清空input的文件  
    uploadDisabled.value = true
    editDiasabled.value = true
    renderDis.value = true
    editDis.value = true
    parseProgress.value = 0
    renderProgress.value = 0
    tableLoading.value = false
}

let emit = defineEmits(['uploadFile', 'closeDialog', 'batchUpdate', 'batchAdd', 'downloadTemplate'])

let closeDialog = () => emit('closeDialog')

let tableData = reactive({}) //用于接收读取的文件对象
let excelData = ref([]) //用于接收读取后的excel文件
let parseProgress = ref(0);  // 文件解析进度
let renderProgress = ref(0); // 表格渲染进度

//获取input选择的文件
const getFile = (e) => {
    tableData = e.target.files[0]
    uploadDisabled.value = false
    renderDis.value = false
    parseProgress.value = 0;
    renderProgress.value = 0;
}


/**
 * 分批次渲染函数
 * @param data 表格数据
 * @param batchSize 分批次渲染的数量
 * @param keys 表格列的key值数组
 */
const batchRender = (data, batchSize = 100,keys) => {
    excelData.value = []
    let renderedRows = 0
    const totalRows = data.length
    const renderNextBatch = () => {       
        const batch = data.slice(renderedRows, renderedRows + batchSize)      
        excelData.value.push(...batch)  
        let newExeclData = ref([])   
        //通过key值重新组合数据
        excelData.value.forEach((item) => {
            let newItem = {}
            keys.forEach((key,idx) => newItem[key] = Object.values(item)[idx])
            newExeclData.value.push(newItem)           
        })
        renderedRows += batchSize        
        // 更新渲染进度
        renderProgress.value = Math.round((renderedRows / totalRows) * 100)
        if (renderedRows < totalRows) {
            emit('uploadFile', newExeclData) //将表格数据发送至父组件
            requestAnimationFrame(renderNextBatch) // 非阻塞渲染requestAnimationFrame（浏览器内置函数）            
        }else{
            emit('uploadFile', newExeclData)
            tableLoading.value = false
        }
    }
    renderNextBatch()
}

//解析文件，发送至父组件分批次渲染
/**
 * 
 * @param keys 表格列的key值数组
 */
const uploadFile = (keys) => {
    if (!tableData.name) {
        msgOpen("请选择文件！")
    } else {
        tableLoadingShow()
        // 1. 读取 Excel 文件   
        let reader = new FileReader()
        reader.readAsArrayBuffer(tableData);
        //计算解析进度
        reader.onprogress = (e) => {
            if (e.lengthComputable) {
                parseProgress.value = Math.round((e.loaded / e.total) * 100)
            }
        }
        reader.onload = (e) => {
            // 2. 解析 Excel 数据
            // const data = new Uint8Array(e.target.result)
            const data = e.target.result
            const workbook = XLSX.read(data, { 
                type: 'array' ,// 以二进制流方式读取获得整份excel表格对象
                cellDates: true,
                dateNF: 'yyyy-mm-dd'
            })
            const worksheet = workbook.Sheets[workbook.SheetNames[0]] //获取excel中第一张表名称
            //获取excel中第一张表数据，数组类型，每一行数据都是一个对象，{ defval: ''}空值也能读取
            const jsonData = XLSX.utils.sheet_to_json(worksheet, { defval: '', raw: false, dateNF: 'yyyy-mm-dd' })
              .map(row => {
                // 处理日期格式
                Object.entries(row).forEach(([key, value]) => {
                  if (value instanceof Date) {
                    props.dateType === "datetime" ? row[key] = XLSX.SSF.format('yyyy-mm-dd HH:MM:SS', value) : row[key] = XLSX.SSF.format('yyyy-mm-dd', value);
                  }
                });
                return row;
              });
            // console.log(jsonData);
            // 3. 分批次渲染（避免一次性渲染卡顿）
            batchRender(jsonData,100,keys);
            editDis.value = false
            editDiasabled.value = false
        }
    }
}
/*
//解析文件，发送至父组件(无分批次渲染)
const uploadFile = () => {
    if (!tableData.name) {
        msgOpen("请选择文件！")
    } else {
        tableLoadingShow()
        // console.log(tableLoading.value);        
        let reader = new FileReader()
        // reader.readAsBinaryString(tableData);
        reader.readAsArrayBuffer(tableData);
        reader.onload = function (e) {
            // console.log(e);            
            let data = e.target.result;
            let wb = XLSX.read(data, {
                type: 'binary', // 以二进制流方式读取获得整份excel表格对象
                cellDates: true 
            });
            let sheet_names = wb.SheetNames;//获取excel中所有表名称
            let sheet1_name = sheet_names[0];//获取excel中第一张表名称
            let sheet01_obj = XLSX.utils.sheet_to_json(wb.Sheets[sheet1_name]);//获取excel中第一张表数据，数组类型，每一行数据都是一个对象
            excelData.value = sheet01_obj
            // console.log(excelData);
            emit('uploadFile', excelData) //将表格数据发送至父组件
            editDis.value = false
            editDiasabled.value = false
        }
    }
}*/

//批量更新
const batchUpdate = () => emit('batchUpdate')

//批量新增
const batchAdd = () => emit('batchAdd')

//渲染EXCEL数据时显示loading
let tableLoading = ref(false)
//显示LOADING
const tableLoadingShow = () => {
    tableLoading.value = true
}

//下载导入模板
const downloadTemplate = () => emit('downloadTemplate')

//向外提供方法或数据
defineExpose({
    open,
    close,
    // tableLoadingShow,
    // tableLoadingNoShow
    //   showLoading,
    //   hideLoading
})

</script>

<style lang="less" scoped>
.el-dialog {
    height: 37.5rem;
    width: 50rem;
    overflow: auto;

    .btn-box {
        height: 10%;
        display: flex;
        justify-content: flex-start;
        align-items: center;

        .input {
            padding: .3125rem;
            color: #fff;
            background-color: #038aff;
            border-color: #038aff;
            border-radius: .1875rem;
            margin-right: .3125rem
        }

        .input:hover {
            color: #038aff;
            background-color: #fff;
            cursor: pointer;
        }

        .input.dis {
            background-color: #e1e3e6;
            border-color: #e1e3e6;
            color: #fff;
        }

        .input.dis:hover {
            cursor: not-allowed;
        }
    }

    .table-box {
        height: 25rem;
        overflow: auto;
        // background-color: aqua;
        position: relative;

        .tableLoading {
            width: 100%;
            height: 100%;
            background-color: rgba(104, 103, 103, 0.5);
            position: absolute;
            left: 0;
            top: 0;
            z-index: 99;
            display: flex;
            flex-direction: column;
            align-items: center;
            justify-content: center;
            span{
                color:#038aff;
            }
        }
    }

    .cbtn-box {
        height: 10%;
    }

}
</style>